function SM4(){

}

SM4.prototype = {
    ib: function (a) {
        var b = [], c, d;
        c = a.length;
        for (var e = 0; e < c; e++)
            d = a.charCodeAt(e),
                65536 <= d && 1114111 >= d ? (b.push(d >> 18 & 7 | 240),
                    b.push(d >> 12 & 63 | 128),
                    b.push(d >> 6 & 63 | 128),
                    b.push(d & 63 | 128)) : 2048 <= d && 65535 >= d ? (b.push(d >> 12 & 15 | 224),
                        b.push(d >> 6 & 63 | 128),
                        b.push(d & 63 | 128)) : 128 <= d && 2047 >= d ? (b.push(d >> 6 & 31 | 192),
                            b.push(d & 63 | 128)) : b.push(d & 255);
        return b
    },
    Sb: function (a, b, c) {
        var d = new Tb();
        b = this.ib(b);
        c = this.ib(c); 
        a = this.ib(a);
        c = d.encrypt_cbc(b, c, a);
        c = base64js.fromByteArray(c);
        null != c && 0 < c.trim().length && c.replace(/(\s*|\t|\r|\n)/g, "");
        return c
    }
}

function Tb() {
        this.sbox = [214, 144, 233, 254, 204, 225, 61, 183, 22, 182, 20, 194, 40, 251, 44, 5, 43, 103, 154, 118, 42, 190, 4, 195, 170, 68, 19, 38, 73, 134, 6, 153, 156, 66, 80, 244, 145, 239, 152, 122, 51, 84, 11, 67, 237, 207, 172, 98, 228, 179, 28, 169, 201, 8, 232, 149, 128, 223, 148, 250, 117, 143, 63, 166, 71, 7, 167, 252, 243, 115, 23, 186, 131, 89, 60, 25, 230, 133, 79, 168, 104, 107, 129, 178, 113, 100, 218, 139, 248, 235, 15, 75, 112, 86, 157, 53, 30, 36, 14, 94, 99, 88, 209, 162, 37, 34, 124, 59, 1, 33, 120, 135, 212, 0, 70, 87, 159, 211, 39, 82, 76, 54, 2, 231, 160, 196, 200, 158, 234, 191, 138, 210, 64, 199, 56, 181, 163, 247, 242, 206, 249, 97, 21, 161, 224, 174, 93, 164, 155, 52, 26, 85, 173, 147, 50, 48, 245, 140, 177, 227, 29, 246, 226, 46, 130, 102, 202, 96, 192, 41, 35, 171, 13, 83, 78, 111, 213, 219, 55, 69, 222, 253, 142, 47, 3, 255, 106, 114, 109, 108, 91, 81, 141, 27, 175, 146, 187, 221, 188, 127, 17, 217, 92, 65, 31, 16, 90, 216, 10, 193, 49, 136, 165, 205, 123, 189, 45, 116, 208, 18, 184, 229, 180, 176, 137, 105, 151, 74, 12, 150, 119, 126, 101, 185, 241, 9, 197, 110, 198, 132, 24, 240, 125, 236, 58, 220, 77, 32, 121, 238, 95, 62, 215, 203, 57, 72];
        this.fk = [2746333894, 1453994832, 1736282519, 2993693404];
    this.ck = [462357, 472066609, 943670861, 1415275113, 1886879365, 2358483617, 2830087869, 3301692121, 3773296373, 4228057617, 404694573, 876298825, 1347903077, 1819507329, 2291111581, 2762715833, 3234320085, 3705924337, 4177462797, 337322537, 808926789, 1280531041, 1752135293, 2223739545, 2695343797, 3166948049, 3638552301, 4110090761, 269950501, 741554753, 1213159005, 1684763257]
 }

Tb.prototype = {
    A: function (a, b, c, d, e) {
        e = b + e > a.length && d + e <= c.length ? a.length - b : d + e > c.length && b + e <= a.length ? c.length - d : b + e <= a.length && d + e <= c.length ? e : c.length < a.length ? c.length - d : a.length - d;
        for (var f = 0; f < e; f++)
            c[f + d] = a[f + b]
    },
    yb: function (a) {
        return [a >> 24 & 255, a >> 16 & 255, a >> 8 & 255, a & 255]
    },
    ka: function (a, b) {
        return b + 3 < a.length ? a[b] << 24 | a[b + 1] << 16 | a[b + 2] << 8 | a[b + 3] : b + 2 < a.length ? a[b + 1] << 16 | a[b + 2] << 8 | a[b + 3] : b + 1 < a.length ? a[b] << 8 | a[b + 1] : a[b]
    },


        expandKey: function(a) {
            for (var b = Array(36), c = Array(Math.ceil(a.length / 4)), d = 0; d < a.length; d++)
                a[d] &= 255;
            for (d = 0; d < c.length; d++)
                c[d] = this.ka(a, 4 * d);
            b[0] = c[0] ^ this.fk[0];
            b[1] = c[1] ^ this.fk[1];
            b[2] = c[2] ^ this.fk[2];
            b[3] = c[3] ^ this.fk[3];
            a = Array(32);
            for (c = 0; 32 > c; c++)
                b[c + 4] = b[c] ^ this.T1(b[c + 1] ^ b[c + 2] ^ b[c + 3] ^ this.ck[c]),
                a[c] = b[c + 4];
            return a
        },
        T1: function(a) {
            var b = Array(4);
            a = this.yb(a);
            b[0] = this.sbox[a[0] & 255];
            b[1] = this.sbox[a[1] & 255];
            b[2] = this.sbox[a[2] & 255];
            b[3] = this.sbox[a[3] & 255];
            b = this.ka(b, 0);
            return b ^ (b << 13 | b >>> 19) ^ (b << 23 | b >>> 9)
        },
        one_encrypt: function(a, b) {
            var c = Array(36);
            c[0] = this.ka(b, 0);
            c[1] = this.ka(b, 4);
            c[2] = this.ka(b, 8);
            c[3] = this.ka(b, 12);
            for (b = 0; 32 > b; b++)
                c[b + 4] = c[b] ^ this.T0(c[b + 1] ^ c[b + 2] ^ c[b + 3] ^ a[b]);
            a = Array(4);
            for (b = 35; 32 <= b; b--)
                a[35 - b] = c[b];
            c = Array(4 * a.length);
            for (b = 0; b < a.length; b++)
                this.A(this.yb(a[b]), 0, c, 4 * b, 4);
            return c
        },
        T0: function(a) {
            a = this.yb(a);
            var b = Array(4);
            b[0] = this.sbox[a[0] & 255];
            b[1] = this.sbox[a[1] & 255];
            b[2] = this.sbox[a[2] & 255];
            b[3] = this.sbox[a[3] & 255];
            a = this.ka(b, 0);
            return a ^ (a << 2 | a >>> 30) ^ (a << 10 | a >>> 22) ^ (a << 18 | a >>> 14) ^ (a << 24 | a >>> 8)
        },
        pkcs7padding: function(a, b) {
            if (null == a)
                return null;
            if (1 === b) {
                var c = 16 - a.length % 16;
                b = Array(a.length + c);
                this.A(a, 0, b, 0, a.length);
                for (var d = 0; d < c; d++)
                    b[a.length + d] = c
            } else
                c = a[a.length - 1],
                b = Array(a.length - c),
                this.A(a, 0, b, 0, a.length - c);
            return b
        },
        encrypt_ecb: function(a, b) {
            if (void 0 === a || 0 !== a.length % 16)
                return console.log("sm4 key is error!"),
                null;
            if (void 0 === b || 0 >= b.length)
                return console.log("data is error!"),
                null;
            a = this.expandKey(a);
            for (var c = parseInt(b.length / 16), d = Array(16 * (c + 1)), e = Array(16), f, g = 0; g < c; g++)
                this.A(b, 16 * g, e, 0, 16),
                f = this.one_encrypt(a, e),
                    this.A(f, 0, d, 16 * g, 16);
            e = Array(b.length % 16);
            0 < e.length && this.A(b, 16 * c, e, 0, b.length % 16);
            b = this.pkcs7padding(e, 1);
            f = this.one_encrypt(a, b);
            this.A(f, 0, d, 16 * c, 16);
            return d
        },
        decrypt_ecb: function(a, b) {
            if (void 0 === a || 0 !== a.length % 16)
                return console.log("sm4 key is error!"),
                null;
            if (void 0 === b || 0 !== b.length % 16)
                return console.log("data is error!"),
                null;
            var c = this.expandKey(a);
            a = Array(32);
            for (var d = 0; d < c.length; d++)
                a[d] = c[32 - d - 1];
            var c = b.length / 16 - 1, e = Array(16), f, g;
            this.A(b, 16 * c, e, 0, 16);
            f = this.one_encrypt(a, e);
            d = this.pkcs7padding(f, 0);
            g = Array(16 * c + d.length);
            this.A(d, 0, g, 16 * c, d.length);
            for (d = 0; d < c; d++)
                this.A(b, 16 * d, e, 0, 16),
                f = this.one_encrypt(a, e),
                    this.A(f, 0, g, 16 * d, 16);
            return g
        },
        encrypt_cbc: function(a, b, c) {
            if (void 0 === a || 0 !== a.length % 16)
                return console.log("sm4 key is error!"),
                null;
            if (void 0 === c || 0 >= c.length)
                return console.log("data is error!"),
                null;
            if (void 0 === b || 0 !== b.length % 16)
                return console.log("iv is error!"),
                null;
            a = this.expandKey(a);
            for (var d = parseInt(c.length / 16), e = Array(16 * (d + 1)), f = Array(16), g = 0; g < d; g++) {
                this.A(c, 16 * g, f, 0, 16);
                for (var h = 0; 16 > h; h++)
                    f[h] ^= b[h];
                b = this.one_encrypt(a, f);
                this.A(b, 0, e, 16 * g, 16)
            }
            g = Array(c.length % 16);
            0 < g.length && this.A(c, 16 * d, g, 0, c.length % 16);
            c = this.pkcs7padding(g, 1);
            for (g = 0; 16 > g; g++)
                c[g] ^= b[g];
            b = this.one_encrypt(a, c);
            this.A(b, 0, e, 16 * d, 16);
            return e
        },
        decrypt_cbc: function(a, b, c) {
            if (void 0 === a || 0 !== a.length % 16)
                return console.log("sm4 key is error!"),
                null;
            if (void 0 === c || 0 !== c.length % 16)
                return console.log("data is error!"),
                null;
            if (void 0 === b || 0 !== b.length % 16)
                return console.log("iv is error!"),
                null;
            a = this.expandKey(a);
            for (var d = Array(32), e = 0; e < a.length; e++)
                d[e] = a[32 - e - 1];
            var f = c.length / 16
              , g = Array(16)
              , h = null;
            a = Array(c.length);
            for (e = 0; e < f; e++) {
                this.A(c, 16 * e, g, 0, 16);
                for (var h = this.one_encrypt(d, g), m = 0; 16 > m; m++)
                    h[m] ^= b[m];
                this.A(g, 0, b, 0, 16);
                this.A(h, 0, a, 16 * e, 16)
            }
            b = this.pkcs7padding(h, 0);
            c = Array(a.length - 16 + b.length);
            this.A(a, 0, c, 0, a.length - 16);
            this.A(b, 0, c, a.length - 16, b.length);
            return c
        }
    };